import 'dart:io';
import 'dart:typed_data';
import 'package:archive/archive_io.dart';
import 'package:path/path.dart' as path;

class ZipUtils {
  /// 将目录打包成 ZIP 文件
  /// [sourceDirPath]：需要压缩的目录路径（绝对路径）
  /// [targetZipPath]：输出 ZIP 文件的路径（绝对路径，如 "/Users/xxx/backup.zip"）
  /// [skipHiddenFiles]：是否跳过隐藏文件（如 .gitignore、.DS_Store）
  static Future<void> compressDirectory({
    required String sourceDirPath,
    required String targetZipPath,
    bool skipHiddenFiles = true,
  }) async {
    // 1. 校验源目录是否存在
    final sourceDir = Directory(sourceDirPath);
    if (!await sourceDir.exists()) {
      throw Exception("源目录不存在：$sourceDirPath");
    }

    // 2. 创建 ZIP 写入器（使用 Deflate 压缩算法，压缩率较高）
    final zipEncoder = ZipFileEncoder();
    // 打开目标 ZIP 文件（不存在则创建，存在则覆盖）
    zipEncoder.open(targetZipPath);

    try {
      // 3. 递归遍历目录下所有文件，添加到 ZIP
      await _addFilesToZip(
        dir: sourceDir,
        zipEncoder: zipEncoder,
        rootDirPath: sourceDirPath, // 用于计算文件在 ZIP 中的相对路径
        skipHiddenFiles: skipHiddenFiles,
      );
    } finally {
      // 4. 关闭 ZIP 写入器（必须执行，否则文件损坏）
      await zipEncoder.close();
    }

    print("压缩完成！ZIP 文件路径：$targetZipPath");
  }
/// 解压缩ZIP文件到指定目录
  /// [zipFilePath]：ZIP文件路径（绝对路径）
  /// [targetDirPath]：解压目标目录（绝对路径）
  /// [overwrite]：是否覆盖已存在的文件（默认true）
  static Future<void> decompressZip({
    required String zipFilePath,
    required String targetDirPath,
    bool overwrite = true,
  }) async {
    // 1. 校验ZIP文件是否存在
    final zipFile = File(zipFilePath);
    if (!await zipFile.exists()) {
      throw Exception("ZIP文件不存在：$zipFilePath");
    }

    // 2. 确保目标目录存在
    final targetDir = Directory(targetDirPath);
    if (!await targetDir.exists()) {
      await targetDir.create(recursive: true);
      print("创建目标目录：$targetDirPath");
    }

    try {
      // 3. 读取ZIP文件内容
      final bytes = await zipFile.readAsBytes();

      // 4. 解析ZIP文件
      final archive = ZipDecoder().decodeBytes(bytes);

      // 5. 遍历ZIP中的所有实体，解压到目标目录
      for (final file in archive) {
        // 计算解压后的文件路径（目标目录 + ZIP内的相对路径）
        final filePath = path.join(targetDirPath, file.name);

        if (file is Directory) {
          // 处理目录：创建对应的文件夹
          final dir = Directory(filePath);
          if (!await dir.exists()) {
            await dir.create(recursive: true);
            print("创建目录：$filePath");
          }
        } else {
          // 处理文件：写入解压内容
          // 检查文件是否已存在，且不需要覆盖
          if (!overwrite && await File(filePath).exists()) {
            print("文件已存在，跳过：$filePath");
            continue;
          }

          // 确保父目录存在
          final parentDir = Directory(path.dirname(filePath));
          if (!await parentDir.exists()) {
            await parentDir.create(recursive: true);
          }

          // 写入文件内容
          await File(filePath).writeAsBytes(Uint8List.fromList(file.content));
          print("解压文件：$filePath");
        }
      }

      print("解压完成！目标目录：$targetDirPath");
    } catch (e) {
      throw Exception("解压失败：$e");
    }
  }
  /// 递归遍历目录，将文件添加到 ZIP
  static Future<void> _addFilesToZip({
    required Directory dir,
    required ZipFileEncoder zipEncoder,
    required String rootDirPath,
    required bool skipHiddenFiles,
  }) async {
    // 遍历目录下所有实体（文件/子目录）
    final entities = dir.list(recursive: false, followLinks: false);

    await for (final entity in entities) {
      final entityPath = entity.path;
      // 计算文件在 ZIP 中的相对路径（保留原目录结构）
      final zipRelativePath = path.relative(entityPath, from: rootDirPath);

      // 跳过隐藏文件/目录（如 .git、.DS_Store）
      final fileName = path.basename(entityPath);
      if (skipHiddenFiles && fileName.startsWith('.')) {
        print("跳过隐藏文件：$entityPath");
        continue;
      }

      if (entity is File) {
        // 5. 处理文件：通过流写入 ZIP（避免大文件占用内存）
        await _addFileToZip(
          file: entity,
          zipEncoder: zipEncoder,
          zipRelativePath: zipRelativePath,
        );
      } else if (entity is Directory) {
        // 6. 处理子目录：递归调用，继续遍历
        await _addFilesToZip(
          dir: entity,
          zipEncoder: zipEncoder,
          rootDirPath: rootDirPath,
          skipHiddenFiles: skipHiddenFiles,
        );
      }
    }
  }

  /// 将单个文件添加到 ZIP（流写入，支持大文件）
  static Future<void> _addFileToZip({
    required File file,
    required ZipFileEncoder zipEncoder,
    required String zipRelativePath,
  }) async {
    try {
      zipEncoder.addFile(file,zipRelativePath);
      print("已添加文件：$zipRelativePath");
    } catch (e) {
      throw Exception("添加文件失败：${file.path}，错误：$e");
    }
  }
}